<?php
/**
 * Zustandsbehafteter nichtdeterministische endliche Automat:
 * Prueft eine Eingabe/Eingabereihenfolge anhand vordefinierter Regeln auf Gueltigkeit
 * 
 * @author		Joachim Ruf
 * @copyright	Copyright (c) 2003-2013 Loresoft Software (http://www.loresoft.de)
 * @license		http://yapary.loresoft.de/lizenzbedingungen     New BSD License
 * 
 * */


Class Yapary_Parser_StateParser
{
	private $arrStartRule;
	private $arrRule;
	private $arrEndRule;
	private $startState;
	private $curState;
	

	function __construct()
	{
		$this->arrStartRule = array();
		$this->arrRule = array();
		$this->arrEndRule = array();
	}
	

	/**
	* Definiert eine Regel, welche immer als Startsymbol vorhanden sein muss
	* @param string $state : Name des Zustands
	* @param string $condition : Zu erfuellende COndition
	* @param string $nextState : Neue State zu welchem bei Erfuellung gewechselt werden soll
	*/	
	public function addStartRule( $state , $condition, $nextState )
	{
		$this->startState = $state;
		$this->arrStartRule[] = array('condition'=>$condition, 'state'=>$state, 'nextState'=>$nextState);
	}
	

	/**
	* Definiert eine Regel, welche immer als Endsymbol vorhanden sein muss
	* @param string $state : Name des Zustands
	* @param string $condition : Zu erfuellende COndition
	*/	
	public function addEndRule( $state, $condition, $nextState = 'done' )
	{
		$this->arrEndRule[] = array('condition'=>$condition, 'state'=>$state, 'nextState'=>$nextState);
	}


	/**
	* Definiert eine allgemeine Regel
	*/	
	public function addRule( $state, $condition, $alphabet, $nextState )
	{
		$this->arrRule[] = array('condition'=>$condition, 'alphabet'=>$alphabet, 'state'=>$state, 'nextState'=>$nextState);
	}
	
	
	/**
	* Prueft den uebergebenen Context auf gueltigkeit
	* @param string $context : Zu pruefender Context
	* @return bool
	*/	
	public function isValid( $context, $separator = ' ' )
	{
		$this->curState = $this->startState;
		if( is_string($context) && strlen($context) != 0 )
		{
			$arr = explode($separator, $context);
			return $this->parse($arr);
		}
		
		return false;
	}
	

	/**
	* Parsed den uz pruefenden Context
	* @param array() $arrData : Zu Pruefendes Array
	* @return bool
	*/	
	private function parse( array $arrData )
	{
		$startPos = 0;
		$stopPos = count($arrData)-1;
		if( count($arrData) >= 2 )	
		{
			if( count($this->arrEndRule) >= 1 ) // Wenn Endregel vorhanden ist
				$stopPos--;

			if( count($this->arrStartRule) >= 1 ) // Wenn Startregel vorhanden ist
			{
				if( !$this->checkSimpleCondition($arrData[0], 'Start') ) // Pruefe alle Regeln und beende mit false, falls eine nicht zutrifft
					return false;
				$startPos++;
			}
			
			for( $i=$startPos; $i<$stopPos; $i++ )
			{
				if( !$this->checkCondition($arrData[$i], $arrData[$i+1]) ) // Pruefe alle Regeln und beende mit false, falls eine nicht zutrifft
					return false;
			} 

			if( count($this->arrEndRule) >= 1 ) // Wenn Startregel vorhanden ist
			{
				if( !$this->checkSimpleCondition($arrData[$stopPos+1], 'End') ) // Pruefe alle Regeln und beende mit false, falls eine nicht zutrifft
					return false;
			}
			
			return true; // Alle Regeln sind erfolgreich zugetroffen
		}
		else
			return false;		
	}

	
	/**
	* Prueft ob Condition1/2 zutreffen
	* @param string $condition1 : 1. Condition
	* @param string $condition2 : 2. Condition
	* @return bool
	*/
	private function checkCondition($condition1, $condition2)
	{
		if( count($this->arrRule) >= 1 )
		{
			foreach( $this->arrRule as $key => $arrData  )
			{
				if( $condition1 == $arrData['condition'] && $condition2 == $arrData['alphabet'] && $this->curState == $arrData['state'])
				{
					$this->curState = $arrData['nextState'];
					return true;
				}
			}
		}
		return false;
	}


	/**
	* Prueft ob Condition eine Start/Stop-Condition ist
	* @param string $condition1 : 1. Condition
	* @param string $check : {Start, Stop} gibt an mit welcher Regeldefinition geprueft werden soll
	* @return bool
	*/
	private function checkSimpleCondition($condition, $check = 'Start')
	{
		$rule = 'arr'.$check.'Rule';
		if( count($this->$rule) >= 1 )
		{
			foreach( $this->$rule as $key => $arrData  )
			{
				if( $condition == $arrData['condition'] && $this->curState == $arrData['state'])
				{
					$this->curState = $arrData['nextState'];
					return true;
				}
			}
		}
		return false;
	}

	
}


?>